package com.example.whitelist.aop;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Optional;

import org.apache.commons.beanutils.BeanUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import com.alibaba.fastjson.JSON;
import com.example.whitelist.annotation.DoWhiteList;


@Aspect
@Component
public class DoJoinPoint {
	
	private Logger logger = LoggerFactory.getLogger(DoJoinPoint.class);
	
	@Autowired(required = false)
	private String whiteListConfig;
	
	/**
	 * 获取当前用户的方法
	 */
	private final static String GET_CURRENT_USER_ID_METHOD = "getCurrentUser";
	
	@Pointcut("@annotation(com.example.whitelist.annotation.DoWhiteList)")
	public void aopPoint() {
	}
	
	@Around("aopPoint()")
	public Object doRouter(ProceedingJoinPoint jp) throws Throwable {
		if(StringUtils.isEmpty(whiteListConfig)){
			return jp.proceed();
		}
		
		// 获取内容
		Method method = getMethod(jp);
		DoWhiteList whiteList = method.getAnnotation(DoWhiteList.class);
		
		// 获取字段值
		String keyValue = getCurrentUserId(jp);
		logger.info("middleware whitelist handler method：{} value：{}", method.getName(), keyValue);
		
		if (StringUtils.isEmpty(keyValue)) return jp.proceed();
		
		String[] split = whiteListConfig.split(",");
		
		// 白名单过滤
		for (String str : split) {
			if (keyValue.equals(str)) {
				return jp.proceed();
			}
		}
		
		// 拦截
		return returnObject(whiteList, method);
	}
	
	/**
	 * 获取当前用户id
	 * @author 徐明龙 XuMingLong 2021-04-08
	 * @param jp
	 * @return java.lang.String
	 */
	private String getCurrentUserId(JoinPoint jp) throws NoSuchMethodException {
		//先尝试从方法中获取用户id
		// 获取内容
		Method method = getMethod(jp);
		DoWhiteList whiteList = method.getAnnotation(DoWhiteList.class);
		// 获取字段值
		String userId = getFiledValue(whiteList.key(), jp.getArgs());
		//没有则尝试从默认方法中获取
		userId = Optional.ofNullable(userId).orElseGet(()->{
			try{
				Method getCurrentUserIdMethod = getCurrentUserIdMethod(jp);
				if(getCurrentUserIdMethod!=null){
					getCurrentUserIdMethod.setAccessible(true);
					return (String)getCurrentUserIdMethod.invoke(jp.getTarget());
				}else{
					logger.error("cannot fetch the method");
					return "";
				}
			}catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e){
				logger.error(e.getMessage());
				return "";
			}
		});
		return userId;
	}
	
	/**
	 * 获取当前用户
	 * @param jp
	 * @return java.lang.reflect.Method
	 */
	private Method getCurrentUserIdMethod(JoinPoint jp) throws NoSuchMethodException {
		try{
			//当前类是否有默认方法
			return jp.getTarget().getClass().getDeclaredMethod(GET_CURRENT_USER_ID_METHOD);
		}catch (NoSuchMethodException e){
			//父类是否有默认方法
			return jp.getTarget().getClass().getSuperclass().getDeclaredMethod(GET_CURRENT_USER_ID_METHOD);
		}
	}
	
	
	private Method getMethod(JoinPoint jp) throws NoSuchMethodException {
		Signature sig = jp.getSignature();
		MethodSignature methodSignature = (MethodSignature) sig;
		return jp.getTarget().getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes());
	}
	
	// 返回对象
	private Object returnObject(DoWhiteList whiteList, Method method) throws IllegalAccessException, InstantiationException {
		Class<?> returnType = method.getReturnType();
		String returnJson = whiteList.returnJson();
		if ("".equals(returnJson)) {
			return returnType.newInstance();
		}
		return JSON.parseObject(returnJson, returnType);
	}
	
	// 获取属性值
	private String getFiledValue(String filed, Object[] args) {
		String filedValue = null;
		for (Object arg : args) {
			try {
				if (StringUtils.isEmpty(filedValue)) {
					filedValue = BeanUtils.getProperty(arg, filed);
				} else {
					break;
				}
			} catch (Exception e) {
				if (args.length == 1) {
					return args[0].toString();
				}
			}
		}
		return filedValue;
	}
	
}
